using System;
using System.Collections.Generic;
using System.Diagnostics;
using static SmartSite.Common.CheckingNullArgument;

namespace SmartSite.Frame
{
    public class BitsVariable
    {
        public List<byte> Buffer { get; }
        public BitUnit Index { get; }
        public BitUnit Count { get; }
        BitUnit CountInByte { get => Index.InByte().ReverseCount(); }
        int IndexByte { get => Index.FloorToByteUnit(); }

        public BitsVariable(List<byte> buffer, BitUnit index, BitUnit count)
        {
            Buffer = buffer;
            Index = index;
            Count = count;

            GrowBuffer();
        }

        void GrowBuffer()
        {
            int countByte = (Index + Count).CeilToByteUnit();
            if (Buffer.Count < countByte)
                for (int i = countByte - Buffer.Count; i > 0; i--)
                    Buffer.Add(0);
        }

        public BitsVariable Clone()
        {
            return new BitsVariable(Buffer, Index, Count);
        }

        public BitsVariable CopyTo(BitsVariable target)
        {
            CheckingNull(target, nameof(target));

            if (Count != target.Count)
                throw new ArgumentException
                (
                    $"目标长度{target.Count}与源长度{Count}不一致",
                    $"{target}.{nameof(target.Count)}"
                );

            int copyCount = Min(Count, CountInByte, target.CountInByte);

            target.SetCurrntBits(GetBitsPart(copyCount), copyCount);

            Reduced(copyCount)?.CopyTo(target.Reduced(copyCount));

            return target;

            int Min(int a, int b, int c) => Math.Min(a, Math.Min(b, c));
        }

        public BitsVariable ChangingIndex(BitUnit index)
        {
            return CopyTo(new BitsVariable(new List<byte>(), index, Count));
        }

        public IEnumerable<BitsPart> BitsParts()
        {
            BitsVariable bitsVariable = Clone();
            while (bitsVariable?.Count > 0)
            {
                yield return bitsVariable.TakeBitsPart(out bitsVariable);
            }
        }

        BitsPart TakeBitsPart(out BitsVariable bitsVariable)
        {
            int count = Math.Min(CountInByte, Count);
            byte bits = GetBitsPart(count);
            bitsVariable = Reduced(count);
            return new BitsPart(bits, count);
        }

        /// <summary>
        /// 返回当前字节内的比特片段
        /// </summary>
        /// <param name="count">需要返回的比特数</param>
        /// <returns></returns>
        byte GetBitsPart(BitUnit count)
        {
            Debug.Assert(count >= 0);
            Debug.Assert(count <= CountInByte);

            byte bits = 0;
            if (Buffer.Count > 0)
            {
                bits = Buffer[IndexByte];
                bits <<= CountInByte.ReverseCount(); // 清除高位
                bits >>= count.ReverseCount(); // 清除低位
            }
            return bits;
        }

        /// <summary>
        /// 保存比特到数组
        /// </summary>
        /// <param name="newBits">要保存到数组的比特串</param>
        /// <param name="count">有效比特串的宽度</param>
        /// <param name="index">有效比特串起始位置</param>
        void SetCurrntBits(byte newBits, BitUnit count)
        {
            Debug.Assert(count <= 8);

            if (Buffer.Count > 0)
            {
                BitUnit lastIndex = CountInByte - count;
                Buffer[IndexByte] ^= (byte)(GetBitsPart(count) << lastIndex); // 清除
                Buffer[IndexByte] ^= (byte)(newBits << lastIndex); // 写入
            }
        }

        /// <summary>
        /// 减少未被处理的比特串长度
        /// </summary>
        /// <param name="count">被减少的比特串长度</param>
        BitsVariable Reduced(BitUnit count)
        {
            return Count - count == 0 ? null : new BitsVariable(Buffer, Index + count, Count - count);
        }
    }
}